#import "PhotoBrowser.h"

#import "Controller.h"
#import "DirData.h"
#import "FolderModel.h"
#import "OperationData.h"
#import "OperationProgressController.h"
#import "PhotoViewer.h"
#import "PreferencesData.h"
#import "SlideshowModel.h"
#import "ThumbData.h"
#import "ThumbnailView.h"
#import "Utility.h"


#import "exif-data.h"
#import "exif.h"
#import "jpeg.h"


// Preference definitions
#define PREF_OUTLINE_RECT		@"Browser.outlineRect"
#define PREF_OUTLINE_WIDTH		@"Browser.savedOutlineWidth"
#define PREF_THUMBNAIL_RECT		@"Browser.thumbnailRect"
#define PREF_THUMBNAIL_WIDTH	@"Browser.savedThumbnailWidth"
#define PREF_SORT_BY			@"Browser.sortBy"
#define PREF_SHOW_DOT_FOLDERS	@"Browser.showDotFolders"
#define PREF_SHOW_NAME			@"Browser.showName"
#define PREF_SHOW_DATE			@"Browser.showDate"
#define PREF_SHOW_FILE_LENGTH	@"Browser.showFileLength"
#define PREF_SHOW_IMAGE_SIZE	@"Browser.showImageSize"


/*
 * Odd - if the outline view (the left one) is allowed to get down to a size of 0,
 * the focus frame no longer draws the right side. So, we limit the size to 3, an
 * empirically determined minimum value that ensures it looks correct (on Panther, 10.3.9...).
 * An additional benefit of not going to zero is that we don't have to track the
 * number of clicks, which is needed when the left view goes to 0, for some reason.
 */
#define MIN_OUTLINE_VIEW_WIDTH	3



//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
@implementation PhotoBrowser

//------------------------------------------------------------------------------
- (void) awakeFromNib
{
	sortBy = SORT_BY_DATE;
	showName = YES;
	showDate = YES;
	showFileLength = NO;
	showImageSize = NO;

	preCreateDirItem = nil;
	showDotFolders = NO;

	loadThreadRunning = NO;
	stopLoadThread = NO;

	[dirDataView setDataSource: self];
	[dirDataView setDelegate: self];
	[thumbView setDelegate: self];
	[mainWindow setDelegate: self];
	[splitView setDelegate: self];

	[dirDataView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
	[dirDataView setMenu: [[Controller sharedController] directoryMenu]];

	[thumbView registerForDraggedTypes: [NSArray arrayWithObjects:NSFilenamesPboardType, nil]];

	regenMismatchedThumbs = [[[Controller sharedController] preferencesData] regenerateMismatchedThumbs];

	NSUserDefaults *userPrefs = [[Controller sharedController] userPreferences];
	getBoolFromPreferences(userPrefs, PREF_SHOW_DOT_FOLDERS, &showDotFolders);
	getBoolFromPreferences(userPrefs, PREF_SHOW_NAME, &showName);
	getBoolFromPreferences(userPrefs, PREF_SHOW_DATE, &showDate);
	getBoolFromPreferences(userPrefs, PREF_SHOW_FILE_LENGTH, &showFileLength);
	getBoolFromPreferences(userPrefs, PREF_SHOW_IMAGE_SIZE, &showImageSize);

	sortBy = getIntFromPreferences(userPrefs, PREF_SORT_BY, SORT_BY_NAME);

	savedOutlineWidth = getIntFromPreferences(userPrefs, PREF_OUTLINE_WIDTH, 200);
	NSString *val = [userPrefs stringForKey: PREF_OUTLINE_RECT];
	if (nil != val)
	{
		NSRect frame = NSRectFromString(val);
		[[[splitView subviews] objectAtIndex: 0] setFrame: frame];	
	}

	savedThumbnailWidth = getIntFromPreferences(userPrefs, PREF_THUMBNAIL_WIDTH, 200);
	val = [userPrefs stringForKey: PREF_THUMBNAIL_RECT];
	if (nil != val)
	{
		NSRect frame = NSRectFromString(val);
		[[[splitView subviews] objectAtIndex: 1] setFrame: frame];
	}

	originalColor = [[thumbView enclosingScrollView] backgroundColor];
	[originalColor retain];
	[self updateColors];

	[[NSNotificationCenter defaultCenter] addObserver
		: self
		selector: @selector (prefsDataChanged:)
		name: PBPrefencesDidChangeNotification
		object: nil];
}

//------------------------------------------------------------------------------
- (void) dealloc
{
	[[NSNotificationCenter defaultCenter] removeObserver: self];

	[originalColor release];
	[topDir release];
	[curDir release];
	[dirRoot release];

	[mainWindow setDelegate: nil];
	[mainWindow close];

	[super dealloc];
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
- (void) clearSelection
{
	[thumbView clearSelection];
}

//------------------------------------------------------------------------------
- (NSString*) currentFolder
{
	return curDir;
}

//------------------------------------------------------------------------------
- (FolderModel*) folderModel
{
	return [thumbView folderModel];
}

//------------------------------------------------------------------------------
- (NSWindow*) mainWindow
{
	return mainWindow;
}

//------------------------------------------------------------------------------
- (NSSize) maxThumbSize
{
	return NSMakeSize([thumbView maxThumbWidth], [thumbView maxThumbHeight]);
}

//------------------------------------------------------------------------------
- (void) redrawThumbnails:(NSArray*) items
{
	// Redraw each selected item
	int count = [items count];
	int idx;
	for (idx = 0; idx < count; ++idx)
	{
		ThumbData *td = [items objectAtIndex: idx];
		[thumbView displayThumbData: td];
	}
}

//------------------------------------------------------------------------------
- (void) selectCurrent
{
	[thumbView selectCurrent];
}

//------------------------------------------------------------------------------
- (NSMutableArray*) selectedItems
{
	return (NSMutableArray*) [thumbView selectedItems];
}

//---------------------------------------------------------------------------------
- (void) thumbnailUpdated:(ThumbData*)thumbData
{
	[super thumbnailUpdated: thumbData];
	[thumbView thumbnailAvailable: thumbData];
}


//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
- (void) prefsDataChanged:(NSNotification*) note
{
	regenMismatchedThumbs = [[[Controller sharedController] preferencesData] regenerateMismatchedThumbs];
	[self updateColors];
	[thumbView display];
}

//------------------------------------------------------------------------------
- (void) updateColors
{
	NSColor *backgroundColor = [[[Controller sharedController] preferencesData] browserBackgroundColor];

	if (nil != backgroundColor)
	{
		[[thumbView enclosingScrollView] setBackgroundColor: backgroundColor];
//		[dirDataView setBackgroundColor: backgroundColor];
	}
	else
	{
		[[thumbView enclosingScrollView] setBackgroundColor: originalColor];
//		[dirDataView setBackgroundColor: originalColor];
	}

	[thumbView setForegroundColor: [[[Controller sharedController] preferencesData] browserTextColor]];
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
- (IBAction) createFolder:(id)sender
{
	preCreateDirItem = [dirDataView itemAtRow: [dirDataView selectedRow]];

	// Add a special item to the outline and go into edit mode.
	NSString *newFolderName = NSLocalizedString(@"untitled folder", @"Browser new folder");
	DirData *dd = [[DirData alloc] initWithName: newFolderName  showDotFolders:showDotFolders];
	[dirRoot addChild: dd];
	[dd release];
	[dirDataView reloadData];

	int row = [dirDataView rowForItem: dd];
	if (row >= 0)
	{
		[dirDataView selectRow: row  byExtendingSelection: NO];
		[dirDataView editColumn:0  row:row  withEvent:nil  select:YES];
	}
}

//------------------------------------------------------------------------------
- (IBAction) openImage:(id)sender
{
	[self openImageInViewer];
}

//------------------------------------------------------------------------------
- (IBAction) refresh:(id)sender
{
	[[thumbView folderModel] refresh];

	disableSelectNotification = YES;
	NSString *originalCurDir = curDir;
	[self loadDirectoryView: topDir];
	disableSelectNotification = NO;

	[self selectDirectoryViewPath: originalCurDir];
}

//------------------------------------------------------------------------------
- (IBAction) revealInFinder:(id)sender
{
	NSString *fullName = nil;

	// If the outline view has focus (is the firstResponder), then reveal
	// the directory selected there. Otherwise, if an image is selected,
	// reveal the selected image.
	DirData *dd = [dirDataView itemAtRow: [dirDataView selectedRow]];
	NSArray *selItems = [thumbView selectedItems];
	if (0 == [selItems count] || [mainWindow firstResponder] == dirDataView)
	{
		fullName = [dd fullPath];
	}
	else
	{
		ThumbData *td = [selItems objectAtIndex: 0];
		fullName = [td fullPath];
	}

	if (nil != fullName)
	{
		[[Controller sharedController] revealInFinder: fullName];
	}
}

//------------------------------------------------------------------------------
- (IBAction) selectAllImages:(id)sender
{
	[thumbView selectAll];
	[self updateStatusBar]; 
}

//------------------------------------------------------------------------------
- (IBAction) showDate:(id)sender
{
}

//------------------------------------------------------------------------------
- (IBAction) showFileSize:(id)sender
{
}

//------------------------------------------------------------------------------
- (IBAction) showImageSize:(id)sender
{
}

//------------------------------------------------------------------------------
- (IBAction) showName:(id)sender
{
}

//------------------------------------------------------------------------------
- (IBAction) sortByDates:(id)sender
{
	if (SORT_BY_DATE != sortBy)
	{
		sortBy = SORT_BY_DATE;
		FolderModel *fm = [FolderModel instance :[self currentFolder]  sort:sortBy  stop:nil];
		[thumbView setFolderModel: fm  addThumbs:YES];
	}
}

//------------------------------------------------------------------------------
- (IBAction) sortByNames:(id)sender
{
	if (SORT_BY_NAME != sortBy)
	{
		sortBy = SORT_BY_NAME;
		FolderModel *fm = [FolderModel instance :[self currentFolder]  sort:sortBy  stop:nil];
		[thumbView setFolderModel: fm  addThumbs:YES];
	}
}

//------------------------------------------------------------------------------
- (IBAction) sortByThumbStates:(id)sender
{
	if (SORT_BY_THUMBSTATE != sortBy)
	{
		sortBy = SORT_BY_THUMBSTATE;
		FolderModel *fm = [FolderModel instance :[self currentFolder]  sort:sortBy  stop:nil];
		[thumbView setFolderModel: fm  addThumbs:YES];
	}
}

//------------------------------------------------------------------------------
- (IBAction) sortByTypes:(id)sender
{
	if (SORT_BY_TYPE != sortBy)
	{
		sortBy = SORT_BY_TYPE;
		FolderModel *fm = [FolderModel instance :[self currentFolder]  sort:sortBy  stop:nil];
		[thumbView setFolderModel: fm  addThumbs:YES];
	}
}

//------------------------------------------------------------------------------
- (IBAction) toggleAutoShow:(id)sender
{
	NSString *folderName = [[thumbView folderModel] path];
	[[Controller sharedController] startAutoSlideshow: folderName];
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
- (PhotoViewer*) openImageInViewer
{
	// If nothing is selected, we open the first image in the directory (if any images exist).
	// If we have a selection, open the first selected image.
	NSString *fullName = nil;

	NSArray *selItems = [thumbView selectedItems];
	if (0 == [selItems count])
	{
		FolderModel *fm = [thumbView folderModel];
		if ([fm count] > 0)
		{
			fullName = [[fm tdAtIndex: 0] fullPath];
		}
		else
		{
			NSBeep();
		}
	}
	else
	{
		fullName = [[selItems objectAtIndex: 0] fullPath];
	}

	if (nil != fullName)
	{
		return [[Controller sharedController] openImageInViewer: fullName];
	}

	return nil;
}

//------------------------------------------------------------------------------
//  OutlineView delegates
//------------------------------------------------------------------------------
- (id)outlineView
		:(NSOutlineView *) outlineView 
		objectValueForTableColumn:(NSTableColumn *) column 
		byItem:(id) item
{
	return [item name];
}

//------------------------------------------------------------------------------
- (int)outlineView
		:(NSOutlineView *)outlineView 
		numberOfChildrenOfItem: (id) item
{
	if (nil == item)
	{
		DirData *dd = dirRoot;
		if (nil == dd)
		{
			return 0;
		}

		return [dd numberOfChildren];
	}
	else
	{
		return [item numberOfChildren];
	}
}

//------------------------------------------------------------------------------
- (BOOL)outlineView
	: (NSOutlineView *) outlineView
	isItemExpandable:(id) item
{
	if (nil == item)
	{
		DirData *dd = dirRoot;
		if (nil == dd)
		{
			return NO;
		}
		else
		{
			// Don't really expect this case...
			return 0 != [dd numberOfChildren];
		}
	}
	else
	{
		return 0 != [item numberOfChildren];
	}
}

//------------------------------------------------------------------------------
- (id)outlineView
		:(NSOutlineView *) outlineView
		child:(int) index 
		ofItem:(id) item
{
	if (nil == item)
	{
		DirData *dd = dirRoot;
		if (nil == dd)
		{
			return 0;
		}

		return [dd childAtIndex:index];
	}
	else
	{
		return [item childAtIndex:index];
	}
}

//------------------------------------------------------------------------------
- (void) outlineViewSelectionDidChange: (NSNotification *)notification
{
	if (nil == curDir || YES == disableSelectNotification)
	{
		return;
	}

	int row = [dirDataView selectedRow];
	DirData *dd = [dirDataView itemAtRow: row];

	if (nil != dd && 
		nil != [dd fullPath] && 
		NO == [curDir isEqualToString: [dd fullPath]])
	{
		[curDir release];
		curDir = [dd fullPath];
		[curDir retain];
		[self loadThumbnails: curDir];
	}
}

//------------------------------------------------------------------------------
- (float)splitView:(NSSplitView*)sender 
		constrainMinCoordinate:(float)proposedMin 
		ofSubviewAt:(int)offset
{
	return MIN_OUTLINE_VIEW_WIDTH;
}

//------------------------------------------------------------------------------
- (BOOL)splitView:(NSSplitView*) sender  canCollapseSubview:(NSView*)subView
{
	NSView *leftView = [[sender subviews] objectAtIndex:0];
	if (subView == leftView)
	{
		NSSplitView *sView = sender;
		NSEvent *evt = [[sView window] currentEvent];
		if (NSLeftMouseDown == [evt type] && 2 == [evt clickCount])
		{
			NSView *leftView = [[sView subviews] objectAtIndex: 0];
			NSView *rightView = [[sView subviews] objectAtIndex: 1];
			NSRect rcOutline = [leftView frame];
			NSRect rcThumbnail = [rightView frame];

			if (NSWidth(rcOutline) <= MIN_OUTLINE_VIEW_WIDTH)
			{
				rcThumbnail.size.width = savedThumbnailWidth;
				rcOutline.size.width = savedOutlineWidth;
			}
			else
			{
				savedOutlineWidth = NSWidth(rcOutline);
				savedThumbnailWidth = NSWidth(rcThumbnail);

				rcThumbnail.size.width += savedOutlineWidth - MIN_OUTLINE_VIEW_WIDTH;
				rcOutline.size.width = MIN_OUTLINE_VIEW_WIDTH;
			}

			[rightView setFrame: rcThumbnail];
			[leftView setFrame: rcOutline];
			[sView adjustSubviews];
		}
	}

	return NO;
}

//------------------------------------------------------------------------------
- (NSDragOperation) outlineView:(NSOutlineView *)ov 
	validateDrop:(id <NSDraggingInfo>)info 
	proposedItem:(id)item 
	proposedChildIndex:(int)childIndex
{
	// Distinguish between internal and foreign drops.
	if (nil == [info draggingSource])
	{
		return [[Controller sharedController] draggingEnteredHelper: info];
	}
	else 
	// Don't accept drops in between rows (we don't insert new items).
	if (NSOutlineViewDropOnItemIndex == childIndex)
	{
		NSPasteboard *pb = [info draggingPasteboard];
		NSString *type = [pb availableTypeFromArray
			: [NSArray arrayWithObject:NSFilenamesPboardType]];

		// Only accept filename drops
		if (nil != type)
		{
			// It can't be dropped into the directory it's alredy in
			if (NO == [curDir isEqualToString: [item fullPath]])
			{
				NSDragOperation opMask = [info draggingSourceOperationMask];
				if (opMask & NSDragOperationMove)
				{
					return NSDragOperationMove;
				}
				else
				if (opMask & NSDragOperationCopy)
				{
					return NSDragOperationCopy;
				}
				else
				{
					return NSDragOperationNone;
				}
			}
		}
	}

	return NSDragOperationNone;
}

//------------------------------------------------------------------------------
- (BOOL) outlineView:(NSOutlineView *)ov 
	acceptDrop:(id <NSDraggingInfo>)info 
	item:(id)item 
	childIndex:(int)childIndex
{
	// Distinguish between internal and foreign drops.
	if (nil == [info draggingSource])
	{
		// This is a drop from another application.
		return [[Controller sharedController] performDragOperationHelper: info];
	}

	BOOL copy = YES;
	BOOL clearSel;
	NSDragOperation opMask = [info draggingSourceOperationMask];
	if (opMask & NSDragOperationMove)
	{
		copy = NO;
		clearSel = YES;
	}
	else
	if (opMask & NSDragOperationCopy)
	{
		copy = YES;
		clearSel = NO;
	}
	else
	{
		return NO;
	}

	[self moveOrCopySelectedItems
		: [item fullPath]  
		copy: copy
		clearSel: clearSel];

	return YES;
}

//------------------------------------------------------------------------------
/**
 * Called when the editing of a cell has ended - we either rename a directory
 * or create it.
 */
- (void) outlineView:(NSOutlineView *)outlineView 
	setObjectValue:(id)object 
	forTableColumn:(NSTableColumn *)tableColumn 
	byItem:(id)item
{
	NSString *oldName = [item name];
	NSString *newFolderName = NSLocalizedString(@"untitled folder", @"Browser new folder");
	if (nil == curDir ||
		(false == [newFolderName isEqualToString: oldName] &&
		[oldName isEqualToString: object]))
	{
		return;
	}

	NSString *parentPath = [curDir stringByDeletingLastPathComponent];
	NSString *newDir = [parentPath stringByAppendingPathComponent: object];
	if ([newFolderName isEqualToString: oldName])
	{
		[[NSFileManager defaultManager] createDirectoryAtPath: newDir  attributes: nil];
	}
	else
	{
		NSString *oldDir = [parentPath stringByAppendingPathComponent: oldName];
		[[NSFileManager defaultManager] movePath
			: oldDir
			toPath: newDir
			handler: nil];
	}

	[item setFullPath: newDir];
	[dirRoot sortChildren];
	[dirDataView reloadData];


	int row = [dirDataView rowForItem: item];
	if (nil != 	preCreateDirItem)
	{
		row = [dirDataView rowForItem: preCreateDirItem];
		preCreateDirItem = nil;
	}

	if (row >= 0)
	{
		[dirDataView selectRow: row  byExtendingSelection: NO];
	}

	[self setOutlineColumnWidth];
}

//------------------------------------------------------------------------------
- (void)outlineViewItemDidExpand:(NSNotification *)notification
{
	[self setOutlineColumnWidth];
}

//------------------------------------------------------------------------------
- (void)outlineViewItemDidCollapse:(NSNotification *)notification
{
	[self setOutlineColumnWidth];
}

//------------------------------------------------------------------------------
//  ThumbnailView delegates
//------------------------------------------------------------------------------
- (void) thumbnailView: (ThumbnailView *) thView 
	selectionChanged: (int) index
	selected:(BOOL) selected
	item: (ThumbData *) td
{
	[self selectionChanged: td];
	[self updateStatusBar];
}

//------------------------------------------------------------------------------
- (void) thumbnailView: (ThumbnailView*) tnView  doubleClicked:(NSString*) file
{
	[[Controller sharedController] openImageInViewer: file];
}

//------------------------------------------------------------------------------
- (NSSplitView*) splitView
{
	return splitView;
}

//------------------------------------------------------------------------------
- (BOOL) hasDirectory
{
	return nil != curDir;
}

//------------------------------------------------------------------------------
- (BOOL) hasSelectedImage
{
	return [thumbView hasSelection];
}

//------------------------------------------------------------------------------
- (BOOL) hasSelectedJPEG
{
	if (NO == [self hasSelectedImage])
	{
		return NO;
	}

	ThumbData *td = [[thumbView selectedItems]  objectAtIndex: 0];
	return  nil != td && FILE_TYPE_JPEG == [td fileType];
}

//------------------------------------------------------------------------------
- (int) sortBy
{
	return sortBy;
}

//------------------------------------------------------------------------------
- (BOOL) validateMenuItem:(id <NSMenuItem>) menuItem
{
	BOOL enabled = NO;

	switch ([menuItem tag] & MN_TAG_MASK)
	{
		case MN_TAG_ALWAYS:
			enabled = YES;
			break;
		case MN_TAG_PATH:
			enabled = [self hasDirectory];
			break;
		case MN_TAG_IMAGE:
			enabled = [self hasSelectedImage];
			break;
		case MN_TAG_JPEG:
			enabled = [self hasSelectedJPEG];
			break;
	}

	[[Controller sharedController] updateSpecialMenus: (NSMenuItem*) menuItem];

	return enabled;
}

//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
- (void) loadDirectoryAndSelect: (NSString *) path  parentDir:(NSString*)parentDir
{
	[curDir release];
	curDir = nil;

	// Add the directory to our Recent files list.
	[[NSDocumentController sharedDocumentController] 
		noteNewRecentDocumentURL: [NSURL fileURLWithPath: path]];

	if (nil != parentDir)
	{
		[self loadDirectoryView: parentDir];
	}
	else
	{
		NSString *parentPath = [path stringByDeletingLastPathComponent];
		[self loadDirectoryView: parentPath];
		[self selectDirectoryViewPath: path];

		[topDir release];
		topDir = [parentPath copy];
	}

	curDir = [path copy];
	[curDir retain];
	[self loadThumbnails: curDir];

}

//------------------------------------------------------------------------------
- (void) loadDirectoryView: (NSString*) path
{
	[dirRoot release];

	dirRoot = [[DirData alloc] initWithPath: path  showDotFolders:showDotFolders];
	[dirDataView reloadData];

	[self setOutlineColumnWidth];
}

//------------------------------------------------------------------------------
- (void) setOutlineColumnWidth
{
	NSCell *cell = [[dirDataView outlineTableColumn] dataCell];

	int maxLevel = 0;
	int maxWidth = 0;

	int count = [dirDataView numberOfRows];
	int idx;
	for (idx = 0; idx < count; ++idx)
	{
		NSString *str = [[dirDataView itemAtRow: idx] name];
		[cell setStringValue: str];
		int width = [cell cellSize].width;
		if (width > maxWidth)
		{
			maxWidth = width;
		}

		int rowLevel = [dirDataView levelForRow: idx];
		if (rowLevel > maxLevel)
		{
			maxLevel = rowLevel;
		}
	}

	++maxLevel;
	maxWidth += maxLevel * [dirDataView indentationPerLevel];
	[[dirDataView outlineTableColumn] setWidth: maxWidth];
}

//------------------------------------------------------------------------------
- (void) selectDirectoryViewPath:(NSString*) path
{
	NSString *prefixPath = [path substringToIndex:[[dirRoot fullPath] length]];
	if (NSOrderedSame != [prefixPath caseInsensitiveCompare: [dirRoot fullPath]])
	{
		NSLog(@"Select path (%@) does not start with the current root (%@)", path, [dirRoot fullPath]);
		return;
	}

	NSArray *pathArray = [path pathComponents];
	int pathIndex = [[[dirRoot fullPath] pathComponents] count];

	int row = -1;
	DirData *cur = dirRoot;
	for (; pathIndex < [pathArray count]; ++pathIndex)
	{
		BOOL match = NO;
		int count = [cur numberOfChildren];
		int idx;
		for (idx = 0; idx < count; ++idx)
		{
			DirData *dd = [cur childAtIndex: idx];
			NSString *element = [pathArray objectAtIndex: pathIndex];

			if ([element isEqualToString: [dd name]])
			{
				cur = dd;
				row = [dirDataView rowForItem: dd];

				if (-1 != row)
				{
					match = YES;
					if ((pathIndex + 1) < [pathArray count])
					{
						[dirDataView expandItem: dd];
					}
				}
				break;
			}
		}

		if (NO == match)
		{
			break;
		}
	}

	if (-1 != row)
	{
		NSIndexSet *indexes = [[NSIndexSet alloc] initWithIndex: row];
		[dirDataView selectRowIndexes: indexes  byExtendingSelection: NO];
		[indexes release];
	}
}

//------------------------------------------------------------------------------
- (void) loadThumbnails: (NSString *) path
{
	NSUserDefaults *userPrefs = [[Controller sharedController] userPreferences];
	[userPrefs setObject: path  forKey: PREF_KEY_LAST_FOLDER];

	if (loadThreadRunning)
	{
		stopLoadThread = YES;

		double waitSpan = 0.05;

		// Wait for up to a second for the thread to stop running.
		int loop;
		for (loop = 0; YES == loadThreadRunning && loop < (int)(1.0 / waitSpan); ++loop)
		{
			[NSThread sleepUntilDate: [NSDate dateWithTimeIntervalSinceNow: waitSpan]];
		}

		stopLoadThread = NO;
	}

	NSArray *pathComponents = [path pathComponents];
	NSString *title;
	int compCount = [pathComponents count];
	if (compCount > 1)
	{
		title = [NSString stringWithFormat
			: @"%@/%@", 
			[pathComponents objectAtIndex: compCount - 2],
			[pathComponents objectAtIndex: compCount - 1]];
	}
	else
	if ([pathComponents count] == 1)
	{
		title = [NSString stringWithFormat: @"%@",
			[pathComponents objectAtIndex: compCount - 1]];
	}
	else
	{
		title = @"/";
	}

	[mainWindow setTitle: title];
	[thumbView clearThumbnails];

	[statusBar setStringValue
			: [NSString stringWithFormat
					: NSLocalizedString(@"Loading images from %@...", @"loadThumbnails"), title]];

	[progressIndicator setHidden: NO];
	[progressIndicator startAnimation: nil];

	loadThreadRunning = YES;
	[NSThread detachNewThreadSelector
		: @selector(loadThumbnailsThread:)
		toTarget: self
		withObject: path];
}

//---------------------------------------------------------------------------------
// 'id' is a FolderModel
- (void) loadThumbnailStarted:(id)fm
{
	[thumbView setFolderModel: fm  addThumbs:NO];

	unsigned count = [fm count];
	if (count > 0 && [fm queueCount] > 0)
	{
		[statusBar setStringValue
				: [NSString stringWithFormat
						:NSLocalizedString(@"Loading %d images...", @"loadThumbnailStarted"), count]];
	}
	else
	{
		[progressIndicator stopAnimation: nil];
		[progressIndicator setHidden: YES];
		[self updateStatusBar];
	}

	[FolderModel releaseInstance: fm];
}

//---------------------------------------------------------------------------------
- (void) folderModelThumbsCompleted: (NSNotification*) note
{
	if ([thumbView folderModel] != [note object])
	{
NSLog(
	@"folderModelThumbsCompleted notification for wrong model (have %@ vs %@)", 
	[[thumbView folderModel] path],
	[[note object] path]);

		return;
	}

	[progressIndicator stopAnimation: nil];
	[progressIndicator setHidden: YES];
	[self updateStatusBar];
}

//---------------------------------------------------------------------------------
/*
	Initiates the loading of files/thumbnails by first getting the basic (fast) 
	data loaded. The FolderModel will load/generate the thumbnails in a background
	thread.
 */
- (void) loadThumbnailsThread: (id) path
{
	NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];


//NSLog(@"loadThumbnailsThread: %@", path);
	FolderModel *fm = [FolderModel instance :path  sort:sortBy  stop:&stopLoadThread];

	[[NSNotificationCenter defaultCenter] addObserver
		: self
		selector: @selector (folderModelThumbsCompleted:)
		name: FMThumbsCompletedNotification
		object: fm];

	// Don't notify the main thread that we're about to load thumbnails if we were asked to stop
	if (NO == stopLoadThread)
	{
		[self performSelectorOnMainThread
			: @selector(loadThumbnailStarted:)
			withObject: fm
			waitUntilDone: NO];
	}


	loadThreadRunning = NO;
//NSLog(@"finished loading thumbs");

	[pool release];
}

//------------------------------------------------------------------------------
- (void) updateStatusBar
{
	NSRect frame = [statusBar frame];
	if (frame.origin.x != 0)
	{
		frame.origin.x = 0;
		[statusBar setFrame: frame];
	}

	FolderModel *fm = [thumbView folderModel];
	int count = [fm count];

	
	UInt64 length = 0;
	int idx;
	for (idx = 0; idx < count; ++idx)
	{
		ThumbData *td = [fm tdAtIndex: idx];
		length += [td fileLength];
	}
	NSString *strLength = formatLength(length);


	NSArray *selItems = [thumbView selectedItems];
	int selCount = [selItems count];
	long selLength = 0;
	for (idx = 0; idx  < selCount; ++idx)
	{
		selLength += [[selItems objectAtIndex: idx] fileLength];
	}
	NSString *strSelLength = formatLength(selLength);


	NSString *text;
	if (selCount > 0)
	{
		if (1 == count)
		{
			NSString *format = NSLocalizedString(@"%u image selected, %@, %ux%u", @"updateStatusBar");
			ThumbData *td = [selItems objectAtIndex: 0];
			text = [NSString stringWithFormat: format, count, strLength, [td width], [td height]];
		}
		else
		{
			NSString *format = NSLocalizedString(@"%u of %d images selected, %@ of %@", @"updateStatusBar");
			text = [NSString stringWithFormat: format, selCount, count, strSelLength, strLength];
		}
	}
	else
	{
		if (count > 1)
		{
			NSString *format = NSLocalizedString(@"%u images, %@", @"updateStatusBar");
			text = [NSString stringWithFormat: format, count, strLength];
		}
		else
		if (count == 1)
		{
			NSString *format = NSLocalizedString(@"%u image, %@", @"updateStatusBar");
			text = [NSString stringWithFormat: format, count, strLength];
		}
		else
		{
			text = NSLocalizedString(@"No images", @"updateStatusBar");
		}
	}

	[statusBar setStringValue: text];
}

//------------------------------------------------------------------------------
- (void) windowWillClose:(NSNotification*) notification
{
	NSUserDefaults *userPrefs = [[Controller sharedController] userPreferences];
	[userPrefs setInteger: sortBy forKey: PREF_SORT_BY];
	[userPrefs setBool: showName forKey: PREF_SHOW_NAME];
	[userPrefs setBool: showDate forKey: PREF_SHOW_DATE];
	[userPrefs setBool: showFileLength forKey: PREF_SHOW_FILE_LENGTH];
	[userPrefs setBool: showImageSize forKey: PREF_SHOW_IMAGE_SIZE];

	NSRect frame = [mainWindow frame];
	[userPrefs setObject: NSStringFromRect(frame)  forKey: PREF_BROWSER_FRAME_RECT];

	frame = [[[splitView subviews] objectAtIndex: 0] frame];
	[userPrefs setObject: NSStringFromRect(frame)  forKey: PREF_OUTLINE_RECT];
	[userPrefs setInteger: savedOutlineWidth  forKey: PREF_OUTLINE_WIDTH];

	frame = [[[splitView subviews] objectAtIndex: 1] frame];
	[userPrefs setObject: NSStringFromRect(frame)  forKey: PREF_THUMBNAIL_RECT];
	[userPrefs setInteger: savedThumbnailWidth  forKey: PREF_THUMBNAIL_WIDTH];

	[[Controller sharedController] browserClosing: self];
}

@end
